﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Crystal
{
    class Vector3d
    {
        /// <summary>
        /// Vector modulus
        /// </summary>
        private double _Ro;        
        /// <summary>
        /// Vector angle between the projection to the xy plane and y axes
        /// </summary>
        private double _phi;       
        /// <summary>
        /// Vector angle between the vector and z axes
        /// </summary>
        private double _teta;       

        /// <summary>
        /// projection to the x axes
        /// </summary>
        private double _Vx;
        /// <summary>
        /// projection to the y axes
        /// </summary>
        private double _Vy;
        /// <summary>
        /// projection to the z axes
        /// </summary>
        private double _Vz;        

        /// <summary>
        /// x coordinate of starting point
        /// </summary>
        private double _start_x;
        /// <summary>
        /// y coordinate of starting point
        /// </summary>
        private double _start_y;
        /// <summary>
        /// z coordinate of starting point
        /// </summary>
        private double _start_z;        

        /// <summary>
        /// x coordinate of ending point
        /// </summary>
        private double _end_x;
        /// <summary>
        /// y coordinate of ending point
        /// </summary>
        private double _end_y;
        /// <summary>
        /// z coordinate of ending point
        /// </summary>
        private double _end_z;

        private static double _accuracy = 1e-10;

        #region avalable fields
        public double Ro { get { return _Ro; } }
        public double phi { get { return Math.Asin((double)_Vy / Math.Sqrt(_Ro * _Ro - _Vz * _Vz)); } }
        public double teta { get { return Math.Acos((double)_Vz / _Ro); } }
        public double[] RadiusVector { get { return new double[3] { _Vx, _Vy, _Vz }; } }
        public double[] StartPoint { get { return new double[3] { _start_x, _start_y, _start_z }; } }
        public double[] EndPoint { get { return new double[3] { _end_x, _end_y, _end_z }; } }
        #endregion

        public Vector3d()
        {
            _start_x = 0;
            _start_y = 0;
            _start_z = 0;
            _end_x = 0;
            _end_y = 0;
            _end_z = 0;
            _Vx = 0;
            _Vy = 0;
            _Vy = 0;
            _Ro = 0;
            _teta = 0;
            _phi = 0;
        }

        public Vector3d(double[] startingPoint, double[] endingPoint)
        {
            if (startingPoint.Length != 3 || endingPoint.Length != 3)
                throw new Exception("Wrong number of gegrees of freedom in vector creation procedure");
            
            _start_x = startingPoint[0];
            _start_y = startingPoint[1];
            _start_z = startingPoint[2];
            _end_x = endingPoint[0];
            _end_y = endingPoint[1];
            _end_z = endingPoint[2];

            _Vx = _end_x - _start_x;
            _Vy = _end_y - _start_y;
            _Vy = _end_z - _start_z;

            _Ro = Math.Sqrt(_Vx * _Vx + _Vy * _Vy + _Vz * _Vz);            
        }

        public Vector3d(double[] startingPoint, double modulus, double teta, double phi)
        {
            if (startingPoint.Length != 3)
                throw new Exception("Wrong number of gegrees of freedom in vector creation procedure");

            this._teta = teta;
            this._phi = phi;
            _start_x = startingPoint[0];
            _start_y = startingPoint[1];
            _start_z = startingPoint[2];
            _Ro = modulus;
            _Vx = _Ro * Math.Sin(_teta) * Math.Cos(_phi);
            _Vy = _Ro * Math.Sin(_teta) * Math.Sin(_phi);
            _Vz = _Ro * Math.Cos(_teta);
        }

        /// <summary>
        /// Creation of radius vector
        /// </summary>
        /// <param name="endingPoint"></param>
        public Vector3d(double[] endingPoint)
        {
            if (endingPoint.Length != 3)
                throw new Exception("Wrong number of gegrees of freedom in vector creation procedure");

            _start_x = 0;
            _start_y = 0;
            _start_z = 0;
            _end_x = endingPoint[0];
            _end_y = endingPoint[1];
            _end_z = endingPoint[2];

            _Vx = _end_x - _start_x;
            _Vy = _end_y - _start_y;
            _Vy = _end_z - _start_z;

            _Ro = Math.Sqrt(_Vx * _Vx + _Vy * _Vy + _Vz * _Vz);
        }

        //Don't know why do I need this function )))
        public void defineAngles()
        {
            _teta = Math.Acos((double)_Vz / _Ro);
            _phi = Math.Asin((double)_Vy / Math.Sqrt(_Ro * _Ro - _Vz * _Vz));
        }

        public Vector3d translate(double[] newStartPoint)
        {
            if (newStartPoint.Length != 3)
                throw new Exception("Wrong number of gegrees of freedom in vector translation");
            double dx = newStartPoint[0] - _start_x;
            double dy = newStartPoint[1] - _start_y;
            double dz = newStartPoint[2] - _start_z;

            double[] newEndPoint = new double[3] 
                { _end_x + dx, _end_y + dy, _end_z + dz };
            return new Vector3d(newStartPoint, newEndPoint);
        }

        public static bool operator ==(Vector3d v1, Vector3d v2)
        {
            if ((object)v1 == null || (object)v2 == null)
                return false;
            if (Math.Abs(v1.Ro - v2.Ro) > _accuracy)
                return false;
            if (   Math.Abs(v1._start_x - v2._start_x) > _accuracy
                || Math.Abs(v1._start_y - v2._start_y) > _accuracy
                || Math.Abs(v1._start_z - v2._start_z) > _accuracy)
                return false;
            if (Math.Abs(v1._end_x - v2._end_x) > _accuracy
                || Math.Abs(v1._end_y - v2._end_y) > _accuracy
                || Math.Abs(v1._end_z - v2._end_z) > _accuracy)
                return false;
            return true;
        }

        public static bool operator !=(Vector3d v1, Vector3d v2)
        {
            if (v1 == v2)
                return false;
            else
                return true;
        }

        public static Vector3d operator +(Vector3d v1, Vector3d v2)
        {
            Vector3d rv_2end = new Vector3d(v1.EndPoint);
            Vector3d rv_1start = new Vector3d(v2.StartPoint);
            if (rv_2end != rv_1start)
                v2 = v2.translate(v1.EndPoint);
            double[] newStartP = v1.StartPoint;
            double[] newEndP = v2.EndPoint;
            Vector3d v = new Vector3d(newStartP, newEndP);
            return v;
        }

    }//Vector3d
}
